/**
 * @file unicode.h
 * @author your name (you@domain.com)
 * @brief 
 * @version 0.1
 * @date 2024-04-01
 * 
 * @copyright Copyright (c) 2024
 * 
 */
#ifndef JSASM_STRINGS_UNICODE_H_
#define JSASM_STRINGS_UNICODE_H_

#include <sys/types.h>

#include "src/base/bit-field.h"
#include "src/common/globals.h"
#include "src/third_party/utf8-decoder/utf8-decoder.h"

namespace unibrow {

    using uchar = unsigned int;
    using byte = unsigned char;

    const int kMaxMappingSize = 4;

#ifndef JSASM_INTL_SUPPORT

template <class T, int size = 256>
class Predicate {

};

class Utf16 {
    public:
        static const int kNoPreviousCharacter = -1;
        static inline bool IsSurrogatePair(int lead, int trail) {
            return IsLeadSurrogate(lead) && IsTrailSurrogate(trail);
        }

        static inline bool IsLeadSurrogate(int code) {
            return (code & 0x1ffc00) == 0xd800;
        }

        static inline bool IsTrailSurrogate(int code) {
            return (code & 0x1ffc00) == 0xdc00;
        }

        static inline int CombineSurrogatePair(uchar lead, uchar trail) {
            return 0x10000 + ((lead & 0x3ff) << 10) + (trail & 0x3ff);
        }

        static const uchar kMaxNonSurrogateCharCode = 0xffff;

        static const int kMaxExtraUtf8BytesForOneUtf16CodeUnit = 3;

        static const int kUtf8BytesToCodeASurrogate = 3;

        static inline uint16_t LeadSurrogate(uint32_t char_code) {
            return 0xd800 + (((char_code - 0x10000) >> 10) & 0x3ff);
        }

        static inline uint16_t TrailSurrogate(uint32_t char_code) {
            return 0xdc00 + (char_code & 0x3ff);
        }
};

class Latin1 {
    public:
        static const uint16_t kMaxChar = 0xff;

        static inline uint16_t TryConvertToLatin1(uint16_t c) {
            switch(c) {
                case 0x39c:
                case 0x3bc:
                    return 0xb5;
                case 0x178:
                    return 0xff;
            }
            return c;
        }
};

enum class Utf8Variant : uint8_t {
#if JSASM_ENABLE_WEBASSEMBLY
    kUtf8,
    kUtf8NoTrap,
    kWtf8,    
#endif
    kLossyUtf8,
    kLastUtf8Variant = kLossyUtf8
};

class Utf8 {
    public:
        using State = Utf8DfaDecoder::State;

        static inline uchar Length(uchar chr, int previos);

        static inline unsigned EncodeOneByte(char* out, uint8_t c);

        static inline unsigned Encode(char* out, uchar c, int previous, bool replace_invalid = false);

        static uchar CalculateValue(const byte* str, size_t length, size_t* cursor);

        static const uchar kBadChar = 0xFFFD;

        static const uchar kBufferEmpty = 0x0;

        static const uchar kIncomplete = 0xFFFFFFFC;

        static const unsigned kMaxEncodedSize = 4;

        static const unsigned kMaxOneByteChar = 0x7f;

        static const unsigned kMaxTwoByteChar = 0x7ff;

        static const unsigned kMaxThreeByteChar = 0xffff;

        static const unsigned kMaxFourByteChar = 0x1fffff;

        static const unsigned kBytesSavedByCombiningSurrogates = 2;

        static const unsigned kSizeOfUnmatchedSurrogate = 3;

        static const unsigned kMax8BitCodeUnitSize = 2;

        static inline uchar ValueOf(const byte* str, size_t length, size_t* cursor);

        using Utf8IncrementalBuffer = uint32_t;

        static inline uchar ValueOfIncremental(const byte** cursor, State* state,
            Utf8IncrementalBuffer* buffer);

        static uchar ValueOfIncrementalFinish(State* state);

        static inline bool IsValidCharacter(uchar c);

        static bool ValidateEncoding(const byte* str, size_t length);
};

#endif // JSASM_INTL_SUPPORT

} // namespace unibrow

#endif // JSASM_STRINGS_UNICODE_H_